package cn.shujuhai.cms.service.thymeleaf;

import cn.shujuhai.cms.config.TemplateEngineConfig;
import cn.shujuhai.cms.domain.*;
import cn.shujuhai.cms.mapper.InfoArticleMapper;
import cn.shujuhai.cms.mapper.SysChannelMapper;
import cn.shujuhai.cms.mapper.SysTemplateMapper;
import cn.shujuhai.cms.mapper.SysTemplateTypeMapper;
import cn.shujuhai.cms.service.InfoChannelService;
import cn.shujuhai.common.aop.SysLogAspect;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@RequiredArgsConstructor
public class StaticHtmlService {
    private final TemplateEngineConfig templateEngine;
    private final ChannelTool channelTool;
    private final InfoTool infoTool;
    private final SysTool sysTool;
    private final CacheManager cacheManager;
    private final SysChannelMapper sysChannelMapper;
    private final SysTemplateMapper sysTemplateMapper;
    private final SysTemplateTypeMapper sysTemplateTypeMapper;
    private final InfoArticleMapper infoArticleMapper;
    private final InfoChannelService infoChannelService;

    @Value("${server.servlet.context-path}")
    private String base;

    @Value("${template.static.thymeleafStaticUrl}")
    private String thymeleafStaticUrl;

    private final InternationService internationService;

    private static final List<String> LANGUAGE_LIST = Arrays.asList("zh-CN", "en");
    private static final Logger logger = LoggerFactory.getLogger(SysLogAspect.class);
    private static final String TEMPLATE_TYPE_CODE = "LIST";

    /**
     * 初始化网页静态化页
     */
    public void initStaticHtml() {
        try {
            deleteDirectory(Paths.get(thymeleafStaticUrl).toFile());
        } catch (IOException e) {
            logger.error("删除静态化目录失败，{}", e.getMessage());
            throw new RuntimeException("删除静态化目录失败", e);
        }
        List<SysChannel> sysChannelList = this.sysChannelMapper.selectList(null);
        for (SysChannel sysChannel : sysChannelList) {
            this.channelStaticHtml(sysChannel);
            SysTemplate sysTemplate = this.sysTemplateMapper.selectById(sysChannel.getDetailTemplate());
            if (sysTemplate != null) {
                initChannelAllArticleStaticHtml(sysChannel, sysTemplate.getTemplateUrl());
            }
        }
    }

    /**
     * 初始化栏目网页静态化页
     */
    @Async("staticHtmlExecutor")
    public void initChannelStaticHtml() {
        try {
            deleteDirectory(Paths.get(thymeleafStaticUrl, "channel").toFile());
        } catch (IOException e) {
            logger.error("删除静态化页面失败，{}", e.getMessage());
            throw new RuntimeException("删除静态化页面失败", e);
        }
        List<SysChannel> sysChannelList = sysChannelMapper.selectList(null);
        System.out.println("______0-0()((8" + sysChannelList);
        sysChannelList.forEach(this::channelStaticHtml);
    }


    /**
     * 初始化栏目下所有文章信息
     *
     * @param sysChannel        栏目信息
     * @param detailTemplateUrl 文章详细页模板路径
     */
    public void initChannelAllArticleStaticHtml(SysChannel sysChannel, String detailTemplateUrl) {
//        List<InfoArticle> infoArticleList = this.infoArticleMapper.selectList(Wrappers.<InfoArticle>lambdaQuery().eq(InfoArticle::getChannelId, sysChannel.getChannelId()));
        List<InfoChannel> infoArticleList = this.infoChannelService.list(Wrappers.<InfoChannel>lambdaQuery().eq(InfoChannel::getChannelId, sysChannel.getChannelId()));
        for (InfoChannel infoArticle : infoArticleList) {
            for (String language : LANGUAGE_LIST) {
                Path path = Paths.get(thymeleafStaticUrl, "article", language, sysChannel.getUrlKey(), infoArticle.getArticleId() + ".html");
                if (!Files.exists(path)) {
                    articleStaticHtml(sysChannel, detailTemplateUrl, infoArticle.getArticleId());
                }
            }
        }
    }

    /**
     * 栏目网页静态化
     *
     * @param sysChannel 栏目信息
     */
    public void channelStaticHtml(SysChannel sysChannel) {
        SysTemplate sysTemplate = sysTemplateMapper.selectById(sysChannel.getTemplateId());
        String templateUrl = sysTemplate.getTemplateUrl();

        TemplateEngine dynamicTemplateEngine = templateEngine.getTemplateEngine();
        Cache cache = cacheManager.getCache("setting");
        String pageNum = cache != null ? cache.get("PAGE_NUM", String.class) : "1";
        String pageSize = cache != null ? cache.get("PAGE_SIZE", String.class) : "5";

        Long num = checkIsList(sysChannel, sysTemplate);
        long pageN = 0;
        long pageS = 0;
        long pageTotal;
        if (num != -1) {
            pageN = Long.parseLong(pageNum != null ? pageNum : "1");
            pageS = Long.parseLong(pageSize != null ? pageSize : "10");
            pageTotal = (num + pageS - 1) / pageS;
        } else {
            pageTotal = 1;
        }
        for (String language : LANGUAGE_LIST) {
            Context context = new Context();
            setContextVariables(language, context, sysChannel);
            for (String lang : LANGUAGE_LIST) {
                Path staticPath = Paths.get("channel", lang, sysChannel.getUrlKey() + ".html");
                context.setVariable(lang.replace("-", ""), staticPath.toString().replace("\\", "/"));
            }
            for (int i = 0; i < pageTotal; i++) {
                context.setVariable("pageNum", pageN + i);
                context.setVariable("pageSize", pageS);
                String htmlContent = renderTemplate(dynamicTemplateEngine, templateUrl, context);

                // 创建并保存静态文件
                Path staticPath = buildStaticFilePath(language, sysChannel, i);
                saveStaticHtml(staticPath, htmlContent);
            }
        }
    }

    /**
     * 文章网页静态化
     *
     * @param sysChannel        栏目信息
     * @param detailTemplateUrl 详细页模板路径
     * @param articleId         文章ID
     */
    @Async("staticHtmlExecutor")
    public void articleStaticHtml(SysChannel sysChannel, String detailTemplateUrl, Long articleId) {
        TemplateEngine dynamicTemplateEngine = templateEngine.getTemplateEngine();
        for (String language : LANGUAGE_LIST) {
            Context context = new Context();
            context.setVariable("articleId", articleId);
            setContextVariables(language, context, sysChannel);
            for (String lang : LANGUAGE_LIST) {
                Path staticPath = Paths.get("article", lang, sysChannel.getUrlKey(), articleId + ".html");
                context.setVariable(lang.replace("-", ""), staticPath.toString().replace("\\", "/"));
            }
            Path staticPath = Paths.get(thymeleafStaticUrl, "article", language, sysChannel.getUrlKey(), articleId + ".html");
            String htmlContent = renderTemplate(dynamicTemplateEngine, detailTemplateUrl, context);
            saveStaticHtml(staticPath, htmlContent);

            // 更新栏目的静态页面
            channelStaticHtml(sysChannel);
        }
    }

    /**
     * 删除文章静态化页面
     *
     * @param sysChannel 栏目信息
     * @param articleId  文章ID
     */
    @Async
    public void deleteArticleStaticHtml(SysChannel sysChannel, Long articleId) {
        for (String language : LANGUAGE_LIST) {
            if (sysChannel.getDetailTemplate() == null) {
                File channelFile = Paths.get(thymeleafStaticUrl, "channel", language, sysChannel.getUrlKey() + ".html").toFile();
                if (channelFile.exists()) {
                    if (!channelFile.delete()) {
                        logger.error("删除一篇文章静态化页面失败！{}", channelFile.getAbsolutePath());
                        throw new RuntimeException("删除一篇文章静态化页面失败！" + channelFile.getAbsolutePath());
                    }
                }
            }
            File file = Paths.get(thymeleafStaticUrl, "article", language, sysChannel.getUrlKey(), articleId + ".html").toFile();
            if (file.exists()) {
                if (!file.delete()) {
                    logger.error("删除文章静态化页面失败！{}", file.getAbsolutePath());
                    throw new RuntimeException("删除文章静态化页面失败！" + file.getAbsolutePath());
                }
            }
        }
        // 更新栏目的静态页面
        channelStaticHtml(sysChannel);
    }

    /**
     * 删除栏目静态化页面及目录
     *
     * @param sysChannel 栏目信息
     */
    public void deleteChannelStaticHtml(SysChannel sysChannel) {
        for (String language : LANGUAGE_LIST) {
            File file = Paths.get(thymeleafStaticUrl, "channel", language, sysChannel.getUrlKey() + ".html").toFile();
            if (file.exists()) {
                if (!file.delete()) {
                    logger.error("删除栏目静态化页面失败！{}", file.getAbsolutePath());
                    throw new RuntimeException("删除栏目静态化页面失败！" + file.getAbsolutePath());
                }
                File filePage = Paths.get(thymeleafStaticUrl, "channel", language, sysChannel.getUrlKey()).toFile();
                try {
                    deleteDirectory(filePage);
                } catch (IOException e) {
                    logger.error("删除栏目静态化页面失败，{}", e.getMessage());
                    throw new RuntimeException("删除栏目静态化页面失败", e);
                }
            }
        }
    }

    /**
     * 删除文章静态化目录
     *
     * @param sysChannel 栏目信息
     */
    public void deleteArticleFileStaticHtml(SysChannel sysChannel) {
        for (String language : LANGUAGE_LIST) {
            File file = Paths.get(thymeleafStaticUrl, "article", language, sysChannel.getUrlKey()).toFile();
            if (file.exists()) {
                try {
                    deleteDirectory(file);
                } catch (IOException e) {
                    logger.error("删除文章静态化目录失败，{}", e.getMessage());
                    throw new RuntimeException("删除文章静态化目录失败", e);
                }
            }
        }
    }

    /**
     * 渲染模板并返回 HTML 内容
     *
     * @param dynamicTemplateEngine 模板引擎
     * @param templateUrl           模板路径
     * @param context               上下文内容
     * @return HTML页面内容
     */
    private String renderTemplate(TemplateEngine dynamicTemplateEngine, String templateUrl, Context context) {
        try {
            return dynamicTemplateEngine.process(templateUrl, context);
        } catch (Exception e) {
            logger.error("模板渲染失败: {}", e.getMessage());
            throw new RuntimeException("模板渲染失败", e);
        }
    }

    /**
     * 构建栏目静态文件路径（分页有关）
     *
     * @param language   语言
     * @param sysChannel 栏目信息
     * @param pageIndex  第几页
     * @return 文件路径
     */
    private Path buildStaticFilePath(String language, SysChannel sysChannel, int pageIndex) {
        if (pageIndex == 0) {
            return Paths.get(thymeleafStaticUrl, "channel", language, sysChannel.getUrlKey() + ".html");
        }
        return Paths.get(thymeleafStaticUrl, "channel", language, sysChannel.getUrlKey(), (pageIndex + 1) + ".html");
    }

    /**
     * 保存静态 HTML 文件
     *
     * @param staticPath  静态文件路径
     * @param htmlContent HTML内容
     */
    private void saveStaticHtml(Path staticPath, String htmlContent) {
        try {
            createDirectoriesIfNeeded(staticPath.getParent());
            Files.writeString(staticPath, htmlContent);
        } catch (IOException e) {
            logger.error("保存静态文件失败: {}", e.getMessage());
            throw new RuntimeException("保存静态文件失败", e);
        }
    }

    /**
     * 确保目标文件夹存在
     *
     * @param parentPath 父文件路径
     * @throws IOException IO异常
     */
    private void createDirectoriesIfNeeded(Path parentPath) throws IOException {
        if (Files.notExists(parentPath)) {
            Files.createDirectories(parentPath);
        }
    }

    /**
     * 检查栏目是否为列表模板
     *
     * @param sysChannel  栏目信息
     * @param sysTemplate 模板信息
     * @return 为列表模板返回对应分页的页数，否返回-1L
     */
    public Long checkIsList(SysChannel sysChannel, SysTemplate sysTemplate) {
        SysTemplateType sysTemplateType = sysTemplateTypeMapper.selectById(sysTemplate.getType());
        if (sysTemplateType.getCode().equals(TEMPLATE_TYPE_CODE)) {
//            Long count = infoArticleMapper.selectCount(Wrappers.<InfoArticle>lambdaQuery()
//                    .in(InfoArticle::getChannelId, sysChannel.getChannelId()));
            long count = this.infoChannelService.count(Wrappers.<InfoChannel>lambdaQuery().eq(InfoChannel::getChannelId, sysChannel.getChannelId()));
            return count > 0 ? count : 1L;
        }
        return -1L;
    }

    /**
     * 设置上下文变量
     *
     * @param language   语言
     * @param context    上下文内容
     * @param sysChannel 栏目信息
     */
    private void setContextVariables(String language, Context context, SysChannel sysChannel) {
        context.setVariable("channel", sysChannel);
        context.setVariable("base", base);
        context.setVariable("channelTool", channelTool);
        context.setVariable("infoTool", infoTool);
        context.setVariable("sysTool", sysTool);
        context.setVariable("state", 1);
        // 国际化支持
        Map<String, String> internationMap = internationService.greeting(language);
        for (Map.Entry<String, String> entry : internationMap.entrySet()) {
            context.setVariable(entry.getKey(), entry.getValue());
        }

        // 设置栏目链接
        setContextHeaderLink(language, context);
        context.setVariable("language", language);
    }

    /**
     * 设置网页头部的栏目导航路径
     *
     * @param language 语言
     * @param context  上下文内容
     */
    public void setContextHeaderLink(String language, Context context) {
        List<SysChannel> sysChannelList = this.sysChannelMapper.selectList(Wrappers.<SysChannel>lambdaQuery()
                .eq(SysChannel::getParentId, 0)
                .eq(SysChannel::getKind, 0)
                .orderByAsc(SysChannel::getChannelSort));
        Map<String, String> channelHeaderLinkMap = new HashMap<>();
        for (SysChannel sysChannel : sysChannelList) {
            List<SysChannel> childList = this.sysChannelMapper.selectList(Wrappers.<SysChannel>lambdaQuery()
                    .eq(SysChannel::getParentId, sysChannel.getChannelId())
                    .orderByAsc(SysChannel::getChannelSort));
            SysChannel child = (childList.isEmpty()) ? null : childList.getFirst();
            if (child != null) {
                sysChannel.setUrlKey(child.getUrlKey());
            }
            Path staticPath = Paths.get("channel", language, sysChannel.getUrlKey() + ".html");
            channelHeaderLinkMap.put(sysChannel.getChannelName(), staticPath.toString().replace("\\", "/"));
        }
        context.setVariable("channelLinkList", channelHeaderLinkMap);
    }


    /**
     * 更新模板关于的静态HTML文件
     *
     * @param sysTemplate 模板信息
     */
    @Async("staticHtmlExecutor")
    public void updateStaticHtml(SysTemplate sysTemplate) {
        SysTemplateType templateType = this.sysTemplateTypeMapper.selectOne(Wrappers.<SysTemplateType>lambdaQuery().eq(SysTemplateType::getTemplateTypeId, sysTemplate.getType()));
        if (templateType.getType() == 0) {  //公共模板
            this.initStaticHtml();
        } else if (templateType.getType() == -1) {  //文章模板
            List<SysChannel> sysChannelList = this.sysChannelMapper.selectList(Wrappers.<SysChannel>lambdaQuery().eq(SysChannel::getDetailTemplate, sysTemplate.getTemplateId()));
            for (SysChannel sysChannel : sysChannelList) {
                this.deleteArticleFileStaticHtml(sysChannel);
                this.initChannelAllArticleStaticHtml(sysChannel, sysTemplate.getTemplateUrl());
            }

        } else {    //栏目模板
            List<SysChannel> sysChannelList = this.sysChannelMapper.selectList(Wrappers.<SysChannel>lambdaQuery().eq(SysChannel::getTemplateId, sysTemplate.getTemplateId()));
            for (SysChannel sysChannel : sysChannelList) {
                this.deleteChannelStaticHtml(sysChannel);
                this.channelStaticHtml(sysChannel);
            }
        }
    }


    /**
     * 删除目录及其内容
     *
     * @param directory 文件
     * @throws IOException IO异常
     */
    private void deleteDirectory(File directory) throws IOException {
        if (directory.exists()) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isFile()) {
                        if (!file.delete()) {
                            throw new IOException("删除文件失败: " + file.getAbsolutePath());
                        }
                    } else {
                        deleteDirectory(file);
                    }
                }
            }
            if (!directory.delete()) {
                throw new IOException("目录删除失败: " + directory.getAbsolutePath());
            }
        }
    }
}
